package com.baomibing.flow.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.baomibing.core.exception.ServerRuntimeException;
import com.baomibing.flow.constant.*;
import com.baomibing.flow.enginee.SubProcess;
import com.baomibing.flow.enginee.TaskJoinProcess;
import com.baomibing.flow.exception.FlowExceptionEnum;
import com.baomibing.flow.execute.Execution;
import com.baomibing.tool.util.Checker;
import com.baomibing.tool.util.ObjectUtil;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * TaskNode
 *
 * @author frog 2023/9/18 14:47
 * @version 1.0.0
 **/
@Slf4j
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ToString
public class TaskNode extends WorkNode {

    @Setter @Getter
    private String id;

    //驳回冲提交后直接跳到目标节点，在重新提交策略为 COMMIT_TO_LATEST_REJECT 及模型为FILL时有效
    @Setter @Getter
    private String directToTarget;

    @Setter @Getter
    private RecommitPolicyEnum recommitPolicy = RecommitPolicyEnum.COMMIT_NEXT_STEP;

    @Setter @Getter
    private AllActionRejectTypeEnum allActionRejectType = AllActionRejectTypeEnum.REJECT_ALL;

    @Setter @Getter
    //委托类型
    private EntrustTypeEnum entrustType = EntrustTypeEnum.USER;
    @Setter @Getter
    //模型 发起申请还是审批
    private TaskModelEnum model = TaskModelEnum.AUDIT;

    @Setter @Getter
    private RejectPolicyEnum rejectPolicy = RejectPolicyEnum.TO_PREVIOUS;

    @Getter
    //委托列表
    private List<String> entrusts;


    @Setter @Getter
    //处理类型
    private ActionTypeEnum actionType = ActionTypeEnum.ANY;

    @Setter @Getter
    //任务类型
    private TaskTypeEnum taskType = TaskTypeEnum.MAIN;

    @JSONField(deserialize=false)
    @Setter @Getter
    private TaskJoinProcess childProcess;

    @Setter @Getter
    private Boolean beAllActionComplete = Boolean.FALSE;

    @Getter @Setter
    private Boolean beAllActionReject = Boolean.FALSE;

    //意见或建议
    @Getter @Setter
    private String taskAdvice;

    public TaskNode withEntrusts(List<String> entrusts) {
        this.entrusts = entrusts;
        return this;
    }

    public void doChildReject(Execution execution) {
        if (actionType == ActionTypeEnum.ALL || actionType == ActionTypeEnum.ALL_ORDER) {
            childProcess.doReject(execution);
            beAllActionReject = true;
            beAllActionComplete = false;
        }
    }

    public void doChildVirtualReject() {
        if (actionType == ActionTypeEnum.ALL || actionType == ActionTypeEnum.ALL_ORDER) {
            beAllActionComplete = false;
            beAllActionReject = false;
        }
    }
    @Override
    public void execute(Execution execution) {
        log.info("*****process: 正在执行TaskNode({}),执行人:{}" ,this.getName(), execution.getActor());
        //意见或建议
        if (Checker.beNotEmpty(execution.getVariables())) {
            String advice = ObjectUtil.toStringIfNotNull(execution.getVariables().get("taskAdvice"));
            if (Checker.beNotEmpty(advice)) {
                taskAdvice = advice;
            }
        }
        //会签或者顺序会签
        if (actionType == ActionTypeEnum.ALL || actionType == ActionTypeEnum.ALL_ORDER) {
            //会签都执行完毕
            if (childProcess.doProcess(execution)) {
                beAllActionComplete = true;
                beAllActionReject = false;
            } else {
                return;
            }
        }
        if (RecommitPolicyEnum.COMMIT_NEXT_STEP == recommitPolicy) {
            List<TransitionNode> outputs = getOutputs();
            for (TransitionNode line : outputs) {
                line.execute(execution);
            }
        } else if (RecommitPolicyEnum.COMMIT_TO_LATEST_REJECT == recommitPolicy) {
            if (Checker.beEmpty(directToTarget)) {
                //节点设置为跳转到上次驳回策略，但跳转节点为空
                 throw new ServerRuntimeException(FlowExceptionEnum.RECOMMIT_TO_LAST_REJECT_POLICY_NOT_FIND_TARGET);
            }
            SubProcess process = this.flow.fillSubProcess2DirectNode(this, directToTarget);
            process.getAction().execute(execution);
            flow.deleteSubProcess(process);

        } else {
            throw new ServerRuntimeException(FlowExceptionEnum.NOT_SUPPORT_THE_RECOMMIT_POLICY, rejectPolicy);
        }
    }

    @Override
    public void reject(Execution execution) {
        log.info("reject: *****正在执行TaskNode({}),执行人:{}" , this.getName(), execution.getActor());
        //会签模式 有一个驳回全部驳回
        if (actionType == ActionTypeEnum.ALL || actionType == ActionTypeEnum.ALL_ORDER) {
            // 全部驳回
            if (AllActionRejectTypeEnum.REJECT_ALL.equals(allActionRejectType)) {
                if (childProcess.doReject(execution)) {
                    beAllActionReject = true;
                } else {
                    return;
                }
            } else if (AllActionRejectTypeEnum.REJECT_SELF.equals(allActionRejectType)) {//只驳回自己
                if (childProcess.doRejectIfMath(execution)) {
                    beAllActionReject = childProcess.beComplete();
                } else {
                    return;
                }
            }

        }
        if (RejectPolicyEnum.TO_PREVIOUS == rejectPolicy) {
            List<TransitionNode> inputs = getInputs();
            for (TransitionNode line : inputs) {
                line.reject(execution);
            }
        } else if (RejectPolicyEnum.TO_FILL_USER == rejectPolicy) {
            //临时添加路径，执行后删除
            SubProcess process = this.flow.fillSubProcess2FillNode(this, recommitPolicy);
            process.getAction().reject(execution);
            flow.deleteSubProcess(process);
        } else {
            throw new ServerRuntimeException(FlowExceptionEnum.NOT_SUPPORT_THE_RECOMMIT_POLICY, rejectPolicy);
        }

    }

    public TaskNode copy() {
        TaskNode t = new TaskNode(name);
        t.setBeAllActionComplete(beAllActionComplete);
        t.setBeAllActionReject(beAllActionReject);
        t.setFlow(flow);
        t.setDescription(description).setStyle(style);
        t.setInputs(inputs).setOutputs(outputs);
        t.setEntrustType(entrustType).setActionType(ActionTypeEnum.ANY).setTaskType(taskType);
        t.withEntrusts(entrusts);
        return t;
    }

    public TaskNode(String name) {
        this.name = name;
        this.nodeType = NodeTypeEnum.Task.name();
    }

    public TaskNode() {
        this.nodeType = NodeTypeEnum.Task.name();
    }


}
